﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using Org.BouncyCastle.Crypto;

namespace LockCrypt.Core {
    public abstract class EncryptionBase : IEncryptionService {
        /// <summary>
        /// Gets the encryption type.
        /// </summary>
        /// <value>The encryption type.</value>
        public virtual EncryptionProviders EncryptionType {
            get { return EncryptionProviders.None; }
        }

        /// <summary>
        /// Gets or sets the encryption key.
        /// </summary>
        /// <value>The encryption key.</value>
        public virtual string EncryptionKey { get; set; }

        #region Method Stubs
        public virtual byte[] Encrypt(byte[] input) {
            return input;
        }
        public virtual byte[] Decrypt(byte[] input) {
            return input;
        }
        #endregion

        protected EncryptionBase() {
            EncryptionKey = string.Empty;
        }

        /// <summary>
        /// Encrypts a string to a byte array.
        /// </summary>
        /// <param name="value">The value to convert</param>
        /// <returns>The encrypted string.</returns>
        public virtual byte[] EncryptString(string value) {
            if(value == null) return null;
            return Encrypt(Encoding.UTF8.GetBytes(value));
        }

        /// <summary>
        /// Decrypts a given value to a string.
        /// </summary>
        /// <param name="value">The value to convert</param>
        /// <returns>The array as a string.</returns>
        public virtual string DecryptString(byte[] value) {
            if(value == null) return null;
            byte[] val = Decrypt(value);
            return Encoding.UTF8.GetString(val, 0, val.Length);
        }


        protected byte[] Encrypt(byte[] input, BufferedBlockCipher bufferedBlockCipher, ICipherParameters cipherParameters) {
            return Process(input, bufferedBlockCipher, cipherParameters, true);
        }

        protected byte[] Decrypt(byte[] input, BufferedBlockCipher bufferedBlockCipher, ICipherParameters cipherParameters) {
            return Process(input, bufferedBlockCipher, cipherParameters, false);
        }

        protected byte[] Process(byte[] input, BufferedBlockCipher bufferedBlockCipher, ICipherParameters cipherParameters, bool forEncryption) {
            bufferedBlockCipher.Init(forEncryption, cipherParameters);

            int inputOffset = 0;
            int inputLength = input.Length;

            int maximumOutputLength = bufferedBlockCipher.GetOutputSize(inputLength);
            byte[] output = new byte[maximumOutputLength];
            int outputOffset = 0;
            int outputLength = 0;

            int bytesProcessed;

            bytesProcessed = bufferedBlockCipher.ProcessBytes(
                    input, inputOffset, inputLength,
                    output, outputOffset
                );
            outputOffset += bytesProcessed;
            outputLength += bytesProcessed;

            bytesProcessed = bufferedBlockCipher.DoFinal(output, outputOffset);
            outputOffset += bytesProcessed;
            outputLength += bytesProcessed;

            if(outputLength == output.Length) {
                return output;
            } else {
                byte[] truncatedOutput = new byte[outputLength];
                Array.Copy(output, 0,
                        truncatedOutput, 0,
                        outputLength
                    );
                return truncatedOutput;
            }
        }
    }
}
